#ifndef POLY_MESH_H
#define POLY_MESH_H
#include <vector>
#include <string>
#include "Vector3D.h"
#include "MeshPoint.h"
#include "../util/NTuple.h"
using namespace std;
using namespace util;

namespace geom
{
	class BBox;
	class FittedBBox;

	/**  @class geom::PolyMesh
	Class to store all necessary information to display a 3D polygonal mesh 
	 */
    class PolyMesh
    {
        protected:
			/** Triangles in the mesh */
			vector< util::Triplet<int> > m_tris; 
			/** Unique points in the mesh */
			vector<MeshPoint> m_points;

			/** Base color for this mesh */
			util::Triplet<float> m_baseColor;

			/** Mesh name */
			string m_name;
        public:
			/** Constructor */
            PolyMesh();
			/** Destructor */
            ~PolyMesh();
			/** Sets the mesh name */
			void setName(string name){m_name = name;}
			/** Gets the mesh name */
			string name() const {return m_name;}

			/** Sets the base color. The color is in the range 0-1 for each of its components*/
			void setBaseColor(float r, float g, float b);
			/** Gets the current base color */
			const util::Triplet<float>& baseColor() const {return m_baseColor;}

			/** Checks if this mesh has normals assignated to its vertices */
			bool hasNormals()const;

			/** Adds another triangle in the mesh. Slow process, as it must check if the points are already 
			 in the mesh, and if they're not, insert them. Use the Triplet<int> version of this method if
			 all the points are already inserted in the mesh */
			void addTriangle(const MeshPoint &p1, const MeshPoint &p2, const MeshPoint &p3);	
			/** Convenience method overload. The same as the previous one, but gets the points in a different format */
			void addTriangle(const util::Triplet<geom::MeshPoint> &tri);	

			/** Forms a triangle in the mesh, based on 3 points that were inserted in it in a previous time */
			void addTriangle(const util::Triplet<int> & indicesTri);

			/** Adds another point to the mesh. NOTE: It does not check if it has already been inserted */
			void addPoint(const MeshPoint &p);
			/** Gets a point, based on its index */
			const MeshPoint& getPoint(int pos) const {return m_points.at(pos);}
			/** Changes a point
			  @param pos Index of the point to change.
			  @param p Point to change it to.*/
			void changePoint(int pos, const MeshPoint &p);
			/** Inserts a point in the mesh, checking if it has already been inserted
			  @return Returns the index of the inserted point */
			int addPointCheckingOthers(const MeshPoint &p);

			/** Gets a triangle from the mesh
			  @param tri Index of the triangle to retrieve */
			util::Triplet<MeshPoint> getTriangle (int tri) const;
			/** Returns the number of tris in the mesh */
			size_t getNumberOfTris() const {return m_tris.size();}
			/** Returns the number of points in the mesh */
			int getNumberOfPoints() const {return m_points.size();}

			/** Gets an Axis-Aligned Bounding cube for the mesh. Note: It has a side*0.1 width empty border on each side */
			BBox getMeshBoundingBox() const;

			/** Gets an Axis-Aligned Bounding Box (not necessarily a cube) for this mesh. */
			FittedBBox getMeshFittedBoundingBox() const;

			/** Read only reference to the mesh points */
			const vector<MeshPoint>& points() const {return m_points;}
			/** Read-Write reference to the mesh points. */
			vector<MeshPoint>& points() {return m_points;}
			/** Read only reference to the mesh triangles */
			const vector< util::Triplet<int> >& triangles() const {return m_tris;}
			/** Read-Write reference to the mesh triangles */
			vector< util::Triplet<int> >& triangles(){return m_tris;}

			/** Sets the normal for a point */
			void setPointNormal(int pos,const Vector3D &normal);

			/** Generates the normals for the whole mesh (very slow) */
			void generateNormals();
    };
}

#endif
